package com.ccp.dev.im.server;

import com.ccp.dev.im.server.connertor.impl.ImConnertorImpl;
import com.ccp.dev.im.server.constant.Constants;
import com.ccp.dev.im.server.model.MessageWrapper;
import com.ccp.dev.im.server.model.proto.MessageProto;
import com.ccp.dev.im.server.proxy.MessageProxy;
import com.ccp.dev.im.server.util.ImUtils;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Sharable
public class ImWebSocketServerHandler extends SimpleChannelInboundHandler<MessageProto.Model>{

	private final static Logger log = LoggerFactory.getLogger(ImWebSocketServerHandler.class);
    private ImConnertorImpl connertor = null;
    private MessageProxy messageProxy = null;
    private static final int TIME_CHANGE = 1000;

    public ImWebSocketServerHandler(MessageProxy proxy, ImConnertorImpl connertor) {
        this.connertor = connertor;
        this.messageProxy = proxy;
    }
	
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object o) throws Exception {
    	 String sessionId = ctx.channel().attr(Constants.SessionConfig.SERVER_SESSION_ID).get();
	    if ( o instanceof IdleStateEvent){
            IdleState state = ((IdleStateEvent) o).state();
            if(state.equals(IdleState.WRITER_IDLE)){
                // 空闲状态事件:写入状态  发送心跳包
                if(StringUtils.isNotEmpty(sessionId)){
                    MessageProto.Model.Builder builder = MessageProto.Model.newBuilder();
                    builder.setCmd(Constants.CmdType.HEARTBEAT);
                    builder.setMsgtype(Constants.ProtobufType.SEND);
                    ctx.channel().writeAndFlush(builder);
                }
                log.debug(IdleState.WRITER_IDLE +"... from "+sessionId+"-->"+ctx.channel().remoteAddress()+" nid:" +ctx.channel().id().asShortText());
            } else if(state.equals(IdleState.READER_IDLE)){
                //空闲状态事件:读取状态   70秒内没收到响应，则关闭连接
                log.debug(IdleState.READER_IDLE +"... from "+sessionId+" nid:" +ctx.channel().id().asShortText());
                Long lastTime = (Long) ctx.channel().attr(Constants.SessionConfig.SERVER_SESSION_HEARBEAT).get();
                if(lastTime == null || ((System.currentTimeMillis() - lastTime)/TIME_CHANGE>= Constants.ImserverConfig.PING_TIME_OUT))
                {
                    connertor.close(ctx);
                }
            }
	    }else {
            super.userEventTriggered(ctx, o);
        }
	}

	@Override
	protected void channelRead0(ChannelHandlerContext ctx, MessageProto.Model message)
			throws Exception {
		  try {
			   String sessionId = connertor.getChannelSessionId(ctx);
                // inbound 请求
                if (message.getMsgtype() == Constants.ProtobufType.SEND) {
                    ctx.channel().attr(Constants.SessionConfig.SERVER_SESSION_HEARBEAT).set(System.currentTimeMillis());
                    MessageWrapper wrapper = messageProxy.convertToMessageWrapper(sessionId, message);
                    if (wrapper != null) {
                        receiveMessages(ctx, wrapper);
                    }
                }
                // outbound 回复
                if (message.getMsgtype() == Constants.ProtobufType.REPLY) {
                	MessageWrapper wrapper = messageProxy.convertToMessageWrapper(sessionId, message);
                	if (wrapper != null) {
                        receiveMessages(ctx, wrapper);
                    }
                }
	        } catch (Exception e) {
	            log.error("ImWebSocketServerHandler channerRead error.", e);
	            throw e;
	        }
		 
	}


	@Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
    	log.info("ImWebSocketServerHandler  join from "+ImUtils.getRemoteAddress(ctx)+" nid:" + ctx.channel().id().asShortText());
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        log.debug("ImWebSocketServerHandler Disconnected from {" +ctx.channel().remoteAddress()+"--->"+ ctx.channel().localAddress() + "}");
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
        log.debug("ImWebSocketServerHandler channelActive from (" + ImUtils.getRemoteAddress(ctx) + ")");
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        super.channelInactive(ctx);
        log.debug("ImWebSocketServerHandler channelInactive from (" + ImUtils.getRemoteAddress(ctx) + ")");
        String sessionId = connertor.getChannelSessionId(ctx);
        receiveMessages(ctx,new MessageWrapper(MessageWrapper.MessageProtocol.CLOSE, sessionId,null, null));  
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.warn("ImWebSocketServerHandler (" + ImUtils.getRemoteAddress(ctx) + ") -> Unexpected exception from downstream." + cause);
    }

    /**
     * to send message
     */
    private void receiveMessages(ChannelHandlerContext hander, MessageWrapper wrapper) {
    	//设置消息来源为Websocket
    	wrapper.setSource(Constants.ImserverConfig.WEBSOCKET);
        if (wrapper.isConnect()) {
       	    connertor.connect(hander, wrapper); 
        } else if (wrapper.isClose()) {
        	connertor.close(hander,wrapper);
        } else if (wrapper.isHeartbeat()) {
        	connertor.heartbeatToClient(hander,wrapper);
        }else if (wrapper.isGroup()) {
        	connertor.pushGroupMessage(wrapper);
        }else if (wrapper.isSend()) {
        	connertor.pushMessage(wrapper);
        } else if (wrapper.isReply()) {
        	connertor.pushMessage(wrapper.getSessionId(),wrapper);
        }  else if (wrapper.isOnline()) {
        	connertor.pushMessage(wrapper);
        }  else if (wrapper.isOffline()) {
        	connertor.pushMessage(wrapper);
        }
    }
}
